/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_DISK
#define _H_DISK

#include "Geometry.h"
#include <cstdio>

//A disk is a solid 2D circle.  Like a triangle or parallelogram it is a planar figure
class Disk : public GeometryObject {
private:

// Realign for proper SIMD access.
// In 32 bits this class is exactly 48 bytes, but because the implicit
// this pointer becomes larger in 64-bit mode, and because structures are 
// aligned, the total lenght of the class will grow up to 64 bytes so
// some more dummy data won't hurt more
#ifdef BRITE_64
  BRITE_UNUSED float dummy1,dummy2,dummy3;
#endif

  float uX,uY,uZ;   //u-axis (in plane of disk)
  float normalX,normalY,normalZ;   //v-axis (in plane of disk, perpendicular to u-axis)
  float radius;
  float centerX,centerY,centerZ;
  int mungedId;

public:
  Disk() { }
  ~Disk() {
    BRITE_DEBUGBREAK;  //geometry objects are now pool allocated and so their destructor should never be called
  }
  void set(float _centerX, float _centerY, float _centerZ, 
    float _normalX, float _normalY, float _normalZ, 
    float _uX, float _uY, float _uZ,
    float _radius, int _id) {
      if (_id&~GID_VALUE_MASK) { printf("bad geometry(p) id %d\n",_id);
      BRITE_DEBUGBREAK;
    }
    uX = _uX; uY = _uY; uZ = _uZ;
    centerX = _centerX; centerY = _centerY; centerZ = _centerZ;
    normalX = _normalX; normalY = _normalY; normalZ = _normalZ;
    radius = _radius;
    mungedId = _id | GID_FLAG_FP;
  }
  static Disk *poolAlloc(PoolAllocator *pool) {
    void *ptr = pool->allocate(sizeof(Disk),SIMD_SIZE);
    return new(ptr) Disk();
  }
  virtual int getType() const { return GEOM_DISK; }
  virtual PrimitiveList *compile(int num,const GeometryObject **array,PoolAllocator *pool)const;
  virtual bool intersectsBox(const AxisAlignedBox &box)const;
  virtual void expandBoxToInclude(AxisAlignedBox *box)const;
  //estimate the bounding box of the portion of the object inside the specified bounding box
  virtual AxisAlignedBox estimateIntersectionBounds(const AxisAlignedBox &box)const;
  //return approximate cost relative to a triangle
  virtual int estimateRelativeCost() const { return 2; }

  int id() const { return mungedId&GID_VALUE_MASK; }
  //get axes and center as SIMD vectors of 4 floats (value of fourth float is not specified)
  F32vec4 getUAxis() const { 
#ifndef BRITE_64
    F32vec4 temp = Load_F32vec4((float *)this);
#else
	F32vec4 temp = Load_F32vec4(&this->dummy3);
#endif
    return _mm_shuffle_ps(temp,temp,_MM_SHUFFLE(3,3,2,1));
  }
  F32vec4 getNormal() const { return Load_F32vec4(&this->normalX); }
  F32vec4 getCenter() const { return Load_F32vec4(&this->centerX); }
  AxisAlignedBox getBoundingBox() const;
  bool intersectsRay(const Ray &ray, IntersectionRecord *hitRec) const;
};


//types used when we compile geometry into PrimitiveLists for fast intersection testing
//disks are not packed very tightly at the moment because we assume that they are not
//very common.  Packing could be improved in the future
class ALIGN16 PackedDisk {
public:
  F32vec4 uAxis;
  F32vec4 normal;
  F32vec4 center;
  float radius;
  int id;
};

//intersection routine for packed disks
void IntersectPackedDisks(class PrimitiveList *, class Ray *ray, class IntersectionRecord *);

class PackedDiskList : public PrimitiveList {
public:
  PackedDisk disks[1]; //actual number will be specified in length field

  static PackedDiskList* poolAlloc(int numDisks, PoolAllocator *pool) {
    int size = sizeof(PrimitiveList) + (numDisks)*sizeof(PackedDisk);
    PackedDiskList *list = (PackedDiskList*)pool->allocate(size);
    memset(list,0,size);
    list->length = numDisks;
    list->setType(GEOM_PACKED_DISK);
    list->func = IntersectPackedDisks;
    return list;
  }
  void setDisk(int index, float centerX, float centerY, float centerZ, 
    float normalX, float normalY, float normalZ,
    float uAxisX, float uAxisY, float uAxisZ, float radius, int id);
};



#endif //_H_DISK

